// @ts-nocheck import { useParams } from 'common' import { ChangeEvent, useEffect, useRef, useState } from 'react' import { AWS_REGIONS } from 'shared-data' import { toast } from 'sonner' import { Button, Checkbox, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from 'ui' import { Admonition } from 'ui-patterns/admonition' import { Input as PasswordInput } from 'ui-patterns/DataInputs/Input' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { isVercelUrl } from '@/components/interfaces/Integrations/Vercel/VercelIntegration.utils' import { Markdown } from '@/components/interfaces/Markdown' import VercelIntegrationWindowLayout from '@/components/layouts/IntegrationsLayout/VercelIntegrationWindowLayout' import { ScaffoldColumn, ScaffoldContainer } from '@/components/layouts/Scaffold' import { PasswordStrengthBar } from '@/components/ui/PasswordStrengthBar' import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query' import { useIntegrationsQuery } from '@/data/integrations/integrations-query' import { useIntegrationVercelConnectionsCreateMutation } from '@/data/integrations/integrations-vercel-connections-create-mutation' import { useVercelProjectsQuery } from '@/data/integrations/integrations-vercel-projects-query' import { useOrganizationsQuery } from '@/data/organizations/organizations-query' import { useProjectCreateMutation } from '@/data/projects/project-create-mutation' import { useDataApiRevokeOnCreateDefaultEnabled, useTrackDefaultPrivilegesExposure, } from '@/hooks/misc/useDataApiRevokeOnCreateDefault' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { usePHFlag } from '@/hooks/ui/useFlag' import { BASE_PATH, PROVIDERS } from '@/lib/constants' import { getInitialMigrationSQLFromGitHubRepo } from '@/lib/integration-utils' import { passwordStrength, PasswordStrengthScore } from '@/lib/password-strength' import { generateStrongPassword } from '@/lib/project' import { useTrack } from '@/lib/telemetry/track' import { useIntegrationInstallationSnapshot } from '@/state/integration-installation' import type { NextPageWithLayout } from '@/types' const VercelIntegration: NextPageWithLayout = () => { return ( <>

New project

) } VercelIntegration.getLayout = (page) => ( {page} ) const CreateProject = () => { const { data: selectedOrganization } = useSelectedOrganizationQuery() const [projectName, setProjectName] = useState('') const [dbPass, setDbPass] = useState('') const [passwordStrengthMessage, setPasswordStrengthMessage] = useState('') const [passwordStrengthScore, setPasswordStrengthScore] = useState(-1) const [shouldRunMigrations, setShouldRunMigrations] = useState(true) const [dbRegion, setDbRegion] = useState(PROVIDERS.AWS.default_region.displayName) const track = useTrack() const snapshot = useIntegrationInstallationSnapshot() const isDataApiRevokeOnCreateDefault = useDataApiRevokeOnCreateDefaultEnabled() const dataApiRevokeOnCreateDefaultFlag = usePHFlag('dataApiRevokeOnCreateDefault') const [dataApiDefaultPrivileges, setDataApiDefaultPrivileges] = useState( !isDataApiRevokeOnCreateDefault ) const hasUserModifiedDataApiDefaultPrivileges = useRef(false) useEffect(() => { if (dataApiRevokeOnCreateDefaultFlag === undefined) return if (hasUserModifiedDataApiDefaultPrivileges.current) return setDataApiDefaultPrivileges(!dataApiRevokeOnCreateDefaultFlag) }, [dataApiRevokeOnCreateDefaultFlag]) const { slug, next, currentProjectId: foreignProjectId, externalId } = useParams() useTrackDefaultPrivilegesExposure({ surface: 'vercel', orgSlug: slug, dataApiDefaultPrivileges, hasUserModified: hasUserModifiedDataApiDefaultPrivileges.current, }) async function checkPasswordStrength(value: string) { const { message, strength } = await passwordStrength(value) setPasswordStrengthScore(strength) setPasswordStrengthMessage(message) } const { mutateAsync: createConnections } = useIntegrationVercelConnectionsCreateMutation() const { data: organizationData } = useOrganizationsQuery() const organization = organizationData?.find((x) => x.slug === slug) /** * array of integrations installed */ const { data: integrationData } = useIntegrationsQuery() /** * the vercel integration installed for organization chosen */ const organizationIntegration = integrationData?.find((x) => x.organization.slug === slug) /** * Vercel projects available for this integration */ const { data: vercelProjects } = useVercelProjectsQuery( { organization_integration_id: organizationIntegration?.id, }, { enabled: organizationIntegration !== undefined } ) function onProjectNameChange(e: ChangeEvent) { e.target.value = e.target.value.replace(/\./g, '') setProjectName(e.target.value) } function onDbPassChange(e: ChangeEvent) { const value = e.target.value setDbPass(value) if (value == '') { setPasswordStrengthScore(-1) setPasswordStrengthMessage('') } else checkPasswordStrength(value) } function generatePassword() { const password = generateStrongPassword() setDbPass(password) checkPasswordStrength(password) } const [newProjectRef, setNewProjectRef] = useState(undefined) const { mutate: createProject } = useProjectCreateMutation({ onSuccess: (res) => { setNewProjectRef(res.ref) track( 'project_creation_simple_version_submitted', { surface: 'vercel', dataApiEnabled: true, dataApiDefaultPrivilegesGranted: dataApiDefaultPrivileges, ...(dataApiRevokeOnCreateDefaultFlag !== undefined && { dataApiRevokeOnCreateDefaultEnabled: dataApiRevokeOnCreateDefaultFlag, }), }, { project: res.ref, organization: res.organization_slug, } ) }, onError: (error) => { toast.error(error.message) snapshot.setLoading(false) }, }) async function onCreateProject() { if (!organizationIntegration) return console.error('No organization installation details found') if (!organizationIntegration?.id) return console.error('No organization installation ID found') if (!foreignProjectId) return console.error('No foreignProjectId set') if (!organization) return console.error('No organization set') snapshot.setLoading(true) let dbSql: string | undefined if (shouldRunMigrations) { const id = toast(`Fetching initial migrations from GitHub repo`) const migrationSql = await getInitialMigrationSQLFromGitHubRepo(externalId) if (migrationSql) dbSql = migrationSql toast.success(`Done fetching initial migrations`, { id }) } createProject({ organizationSlug: organization.slug, name: projectName, dbPass, dbRegion, dbSql, dataApiRevokeDefaultPrivileges: !dataApiDefaultPrivileges, }) } // Wait for the new project to be created before creating the connection const { data, isSuccess } = useProjectSettingsV2Query( { projectRef: newProjectRef }, { enabled: newProjectRef !== undefined, // refetch until the project is created refetchInterval: (query) => { const data = query.state.data return ((data?.service_api_keys ?? []).length ?? 0) > 0 ? false : 1000 }, } ) useEffect(() => { if (!isSuccess) return const onSuccessFunc = async () => { const isReady = (data.service_api_keys ?? []).length > 0 if (!isReady || !organizationIntegration || !foreignProjectId || !newProjectRef) { return } const projectDetails = vercelProjects?.find((x: any) => x.id === foreignProjectId) try { await createConnections({ organizationIntegrationId: organizationIntegration?.id, connection: { foreign_project_id: foreignProjectId, briven_project_ref: newProjectRef, integration_id: '0', metadata: { ...projectDetails, brivenConfig: { projectEnvVars: { write: true, }, }, }, }, orgSlug: selectedOrganization?.slug, }) } catch (error) { console.error('An error occurred during createConnections:', error) return } snapshot.setLoading(false) if (next && isVercelUrl(next)) { window.location.href = next } } onSuccessFunc() }, [data, isSuccess]) return (

Briven project details

} > 0} onChange={onDbPassChange} />
setShouldRunMigrations(!!checked)} />

To get you started quickly, we can create new tables for you with seed (sample) data. You can delete these tables later.

{ hasUserModifiedDataApiDefaultPrivileges.current = true setDataApiDefaultPrivileges(!!checked) }} />

Grants privileges to Data API roles by default, exposing new tables. We recommend disabling this to control access manually.

) } export default VercelIntegration